package org.langcom.io;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class TransferUtils {

	private static final String DIR_SIGN = "$$";

	public static void send(OutputStream os, File fileIn) throws IOException {
		sendInt(os, fileIn, "");
		ByteStream.toStream(os, 0);
	}

	public static void send(OutputStream os, File fileIn, String prefix) throws IOException {
		sendInt(os, fileIn, prefix);
		ByteStream.toStream(os, 0);
	}

	private static void sendInt(OutputStream os, File dir, String prefix) throws IOException {
		if (dir.isFile()) {
			ByteStream.toStream(os, 1);
			ByteStream.toStream(os, prefix + File.separator + dir.getName());
			ByteStream.toStream(os, dir);
		} else {
			File[] listFiles = dir.listFiles();
			int cnt_files = listFiles.length;
			List<File> dirList = new ArrayList<File>();
			ByteStream.toStream(os, cnt_files);
			for (int cur_file = 0; cur_file < cnt_files; cur_file++) {
				File file = listFiles[cur_file];
				if (file.isDirectory()) {
					dirList.add(file);
					ByteStream.toStream(os, DIR_SIGN + prefix + File.separator + file.getName());
				} else {
					ByteStream.toStream(os, prefix + File.separator + file.getName());
					ByteStream.toStream(os, file);
				}
			}
			for (File file : dirList) {
				sendInt(os, file, prefix + File.separator + file.getName());
			}
		}
	}

	public static void receive(InputStream in, String pathToSave) throws IOException, FileNotFoundException {
		int nof_files = ByteStream.toInt(in);
		while (nof_files > 0) {
			for (int cur_file = 0; cur_file < nof_files; cur_file++) {
				String file_name = ByteStream.toString(in);
				if (file_name.startsWith(DIR_SIGN)) {
					new File(pathToSave, file_name.substring(DIR_SIGN.length())).mkdir();
				} else {
					ByteStream.toFile(in, new File(pathToSave, file_name));
				}
			}
			nof_files = ByteStream.toInt(in);
		}
	}

	private static void expore(File fileIn, LinkedList<File> resultIn) {
		if (fileIn.isFile()) {
			resultIn.add(fileIn);
		} else {
			File[] listFiles = fileIn.listFiles();
			int cnt_files = listFiles.length;
			List<File> dirList = new ArrayList<File>();
			for (int cur_file = 0; cur_file < cnt_files; cur_file++) {
				File file = listFiles[cur_file];
				if (file.isDirectory()) {
					dirList.add(file);
				}
				resultIn.add(file);
			}
			for (File file : dirList) {
				expore(file, resultIn);
			}
		}
	}

	public static void sendEx(OutputStream os, File fileIn) throws IOException {
		LinkedList<File> fileList = new LinkedList<File>();
		expore(fileIn, fileList);
		sendIntEx(os, fileIn, fileList);
		ByteStream.toStream(os, 0);
	}

	private static void sendIntEx(OutputStream os, File fileIn, LinkedList<File> resultIn) throws IOException {
		if (fileIn.isFile()) {
			ByteStream.toStream(os, fileIn.getName());
			ByteStream.toStream(os, fileIn);
		} else {
			ByteStream.toStream(os, resultIn.size());
			String rootPath = fileIn.getPath();
			while (!resultIn.isEmpty()) {
				File file = resultIn.poll();
				String relativePath = file.getPath().substring(rootPath.length());
				if (file.isDirectory()) {
					ByteStream.toStream(os, DIR_SIGN + relativePath);
				} else {
					ByteStream.toStream(os, relativePath);
					ByteStream.toStream(os, file);
				}
			}
		}
	}

	public static void sendExProgress(OutputStream os, File fileIn, ITransferListener... listeners) throws IOException {
		LinkedList<File> fileList = new LinkedList<File>();
		expore(fileIn, fileList);
		sendIntExProgress(os, fileIn, fileList, listeners);
		ByteStream.toStream(os, 0);
	}

	private static void sendIntExProgress(OutputStream os, File fileIn, LinkedList<File> resultIn, ITransferListener... listeners) throws IOException {
		if (fileIn.isFile()) {
			ByteStream.toStream(os, fileIn.getName());
			ByteStream.toStream(os, fileIn);
		} else {
			fireStartTransfer(resultIn.size(), listeners);
			ByteStream.toStream(os, resultIn.size());
			String rootPath = fileIn.getPath();
			while (!resultIn.isEmpty()) {
				File file = resultIn.poll();
				String relativePath = file.getPath().substring(rootPath.length());
				if (file.isDirectory()) {
					ByteStream.toStream(os, DIR_SIGN + relativePath);
				} else {
					ByteStream.toStream(os, relativePath);
					ByteStream.toStream(os, file);
				}
				fireTransfer(1, listeners);
			}
		}
	}

	private static void fireTransfer(int work, ITransferListener[] listeners) {
		for (ITransferListener listener : listeners) {
			listener.worked(work);
		}
	}

	private static void fireStartTransfer(int workTotal, ITransferListener[] listeners) {
		for (ITransferListener listener : listeners) {
			listener.setTotalWork(workTotal);
		}
	}
}
